﻿using System;
using System.Collections.Generic;

using System.Text;
using System.Data.SqlClient;
using Estimate.Common;
using Estimate.Common.Entity;
using Estimate.Common.DBFieldTypes;
using Estimate.Common.Repository;
using System.Text.RegularExpressions;
using log4net;

namespace Estimate.Common.Estimate
{
    /// <summary>
    /// 负责根据给定的条件，找出预报值并计算他们的semi
    /// </summary>
    public class Semier
    {
        private ForeRepository[] _repositorys;

        private string _connectionString;


        public static ILog Logger = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

        /// <summary>
        /// 初始化，搜索数据库，找出所有的Fore表
        /// </summary>
        /// <param name="connectionString">数据库连接字符串</param>
        public Semier(string connectionString)
        {
            _connectionString = connectionString;
            List<string> tableNames = new List<string>();

            
            //读取数据库中所有的Fore表
            string selectTableNames = "select name from sysobjects where type = 'U' and name like '%Fore'";

            SqlConnection connection = new SqlConnection(connectionString);
            SqlDataReader reader = DBHelper.ExecuteReader(connection, selectTableNames);
            
            while (reader.Read())
            {
                tableNames.Add(reader["name"].ToString());
            }
            connection.Close();



            _repositorys = new ForeRepository[tableNames.Count];
            int curIndex = 0;
            foreach (string table in tableNames)
            {
                _repositorys[curIndex] = new ForeRepository(connectionString, table);
                curIndex++;
            }                       
        }

        public Semier(string connectionString, ForeRepository[] repositorys)
        {
             _connectionString = connectionString;
             _repositorys = repositorys;
        }

        /// <summary>
        /// 根据数据库表名，判断出应该使用哪套实况站和自动站的对照关系
        /// </summary>
        /// <param name="tableName">数据库表名</param>
        /// <param name="dt">实况的日期, 2011-07-01前，58151站的实况使用58154</param>
        /// <returns>对照类型</returns>
        public static RealStationCompareType GetCompareType(string tableName,ForeSemiType type)
        {
            if (tableName.IndexOf("Fore") == -1)
                return RealStationCompareType.None;

            string suffix = tableName.Substring(tableName.IndexOf("_") + 1);

            if (suffix == "SiteFore" || suffix == "RainFore")
            {
                if (type == ForeSemiType.Auto)
                    return RealStationCompareType.Site_Auto;
                else
                    return RealStationCompareType.Site_Manual;
            }
            else
            {
                if (type == ForeSemiType.Auto)
                    return RealStationCompareType.Normal_Auto;
                else
                    return RealStationCompareType.Normal_Manual;
            }
        }

        /// <summary>
        /// 给定一个雨水实况，找出相应的预报并计算他们的semi
        /// 对于手动站数据，一般来得很迟。这时候value存在自动站里。对于这种情况，需要选择自动站的数据，再更新成手动站数据
        /// </summary>        
        /// <param name="rainEntity">降水实况数据</param>
        /// <param name="selectSemiType">保存到数据库中的降水类型</param>
        /// <param name="saveSemiType">保存时使用的降水类型</param>
        public void CalculatSemiWithRainRecord(RainRealEntity rainEntity,ForeSemiType selectSemiType, ForeSemiType saveSemiType)
        {
            ForeRealLinker linker = new ForeRealLinker();

            WaterPrediction waterPrediction = new WaterPrediction(_connectionString, RealStationCompareType.None);

            foreach (ForeRepository repository in _repositorys)
            {
                foreach (BaseForeRealCompare condition in linker.GetRainRelatedForeRecordConditions(rainEntity.Type, rainEntity.Date))
                {                    
                    foreach(ForeEntity entity in repository.RealValueFilteForeRecord(condition.ForeDate,condition.Clock,condition.Span,condition.Type,selectSemiType,false))
                    {
                        waterPrediction.CompareType = GetCompareType(repository.TableName, saveSemiType);
                        
                        waterPrediction.InputPrediction(entity.Value);

                        waterPrediction.InputReal(rainEntity.Value, saveSemiType);
                        

                        EstimateHourSpan span = condition.Is24Hours ? EstimateHourSpan.H24 : EstimateHourSpan.H12;
                        waterPrediction.PreDealRainReal(span, WaterfallKind.Rain);

                        entity.Semi = waterPrediction.SemiResult;

                        if (entity.Semi.Length == 0)
                        {
                            Logger.Error(FormatInfo("Rain 计算出的Semi为空，请检查", entity));
                        }
                        entity.Semitype = saveSemiType;

                       
                        repository.SetForeRecord(entity);
                        
                    }
                }
            }
        }
    
        /// <summary>
        /// 给定一个气温实况，找出相应的预报并计算他们的semi
        /// 对于手动站数据，一般来得很迟。这时候value存在自动站里。对于这种情况，需要选择自动站的数据，再更新成手动站数据
        /// </summary>        
        /// <param name="rainEntity">气象实况数据</param>
        /// <param name="selectSemiType">保存到数据库中的降水类型</param>
        /// <param name="saveSemiType">保存时使用的降水类型</param>
        public void CalculatSemiWithTempRecord(TempRealEntity tempEntity, ForeSemiType selectSemiType, ForeSemiType saveSemiType) 
        {
            ForeRealLinker linker = new ForeRealLinker();
            TempPrediction tempPrediction = new TempPrediction(_connectionString, RealStationCompareType.None);

            foreach (ForeRepository repository in _repositorys)
            {
                foreach (BaseForeRealCompare condition in linker.GetTempRelatedForeRecordConditions(tempEntity.Type, tempEntity.Date))
                {
                    foreach (ForeEntity entity in repository.RealValueFilteForeRecord(condition.ForeDate, condition.Clock, condition.Span, condition.Type, selectSemiType,false))
                    {
                        tempPrediction.CompareType = GetCompareType(repository.TableName, saveSemiType);

                        tempPrediction.InputPrdiction(entity.Value);
                        tempPrediction.InputReal(tempEntity.Value);

                        EstimateHourSpan span = condition.Is24Hours ? EstimateHourSpan.H24 : EstimateHourSpan.H12;                        

                        entity.Semi = tempPrediction.SemiResult;
                        if (entity.Semi.Length == 0)
                        {
                            Logger.Error(FormatInfo("Temp 计算出的Semi为空，请检查", entity));
                        }

                        entity.Semitype = saveSemiType;


                        repository.SetForeRecord(entity);
                        
                    }
                }
            }
        }

        public static void ReCalculateSemi(ForeRepository repository, ForeEntity[] Records)
        {

            ForeRealLinker linker = new ForeRealLinker();

            RainRealRepository autoRainRealRepository = RainRealRepositoryFactory.AutoStationRainRealRepository(repository.ConnectionString);
            RainRealRepository manualRainRealRepository = RainRealRepositoryFactory.ManualRainRealRepository(repository.ConnectionString);
            TempRealRepository autoTempRealRepository = TempRealRepositoryFactory.AutoStationTempRealRepository(repository.ConnectionString);
            TempRealRepository manualTempRealRepository = TempRealRepositoryFactory.ManualTempRealRepository(repository.ConnectionString);

            WaterPrediction waterPrediction = new WaterPrediction(repository.ConnectionString, RealStationCompareType.None);
            TempPrediction tempPrediction = new TempPrediction(repository.ConnectionString, RealStationCompareType.None);

            foreach (ForeEntity entity in Records)
            {
                if (entity.Type == ForeFactorType.mint || entity.Type == ForeFactorType.maxt)
                {
                    TempForeRealCompare condition = linker.GetForeRelatedTempCondition(entity.Date, entity.Span, entity.Clock, entity.Type);

                    if (condition == null)
                        continue;

                    TempRealRepository tempRealRepository;
                    if (entity.Semitype == ForeSemiType.Auto)
                        tempRealRepository = autoTempRealRepository;
                    else
                        tempRealRepository = manualTempRealRepository;

                    string real = tempRealRepository.GetValue(condition.RealDate, condition.TempType);
                    if (real == "")
                        Logger.Error(string.Format("无法找到 {0}, {1} 对应的实况气温数据。计算出来的Semi会是空的", condition.RealDate, condition.TempType));                       

                    tempPrediction.CompareType = Semier.GetCompareType(repository.TableName, entity.Semitype);

                    tempPrediction.InputPrdiction(entity.Value);
                    tempPrediction.InputReal(real);

                    EstimateHourSpan span = condition.Is24Hours ? EstimateHourSpan.H24 : EstimateHourSpan.H12;

                    entity.Semi = tempPrediction.SemiResult;


                    repository.SetForeRecord(entity);
                }

                if (entity.Type == ForeFactorType.wth)
                {
                    RainForeRealCompare condition = linker.GetForeRelatedRainCondition(entity.Date, entity.Span, entity.Clock);

                    RainRealRepository rainRealRepository;
                    if (entity.Semitype == ForeSemiType.Auto)
                        rainRealRepository = autoRainRealRepository;
                    else
                        rainRealRepository = manualRainRealRepository;

                    string real = rainRealRepository.GetValue(condition.RealDate, condition.RainType);
                    if (real == "")
                        Logger.Error(string.Format("无法找到 {0}, {1} 对应的降水实况数据。计算出来的Semi会是空的", condition.RealDate, condition.RainType));
                    waterPrediction.CompareType = Semier.GetCompareType(repository.TableName, entity.Semitype);

                    waterPrediction.InputPrediction(entity.Value);

                    waterPrediction.InputReal(real, entity.Semitype);


                    EstimateHourSpan span = condition.Is24Hours ? EstimateHourSpan.H24 : EstimateHourSpan.H12;
                    waterPrediction.PreDealRainReal(span, WaterfallKind.Rain);

                    entity.Semi = waterPrediction.SemiResult;

                    repository.SetForeRecord(entity);
                }

            }
        }

        private string FormatInfo(string message, ForeEntity entity)
        {
            return string.Format("{0}, date='{1}' and type='{2}' and semitype='{3}' and sendpoint='{4}' and span='{5}' ", message, entity.Date.ToString("yyyyMMdd"), entity.Type, entity.Semitype, entity.Sendpoint, entity.Span);
        }
    }
}
